package com.salt.modules.sys.service;

import cn.hutool.core.util.RandomUtil;
import com.salt.common.json.AjaxJson;
import com.salt.common.utils.DateUtils;
import com.salt.common.utils.IdGen;
import com.salt.common.utils.Merge;
import com.salt.common.utils.StringUtils;
import com.salt.core.redis.cache.SaltRedis;
import com.salt.core.sms.SmsTemplate;
import com.salt.core.sms.model.SmsCode;
import com.salt.core.sms.model.SmsData;
import com.salt.core.verify.image.constant.ImageConstant;
import com.salt.core.verify.image.utils.ImageUtil;
import com.salt.core.verify.image.utils.IndexUtil;
import com.salt.modules.sys.entity.SmsLog;
import com.salt.modules.sys.mapper.SmsLogMapper;
import com.salt.modules.sys.mapper.UserMapper;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.time.Duration;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @Description 用户手机验证码业务类
 * @Author salt
 * @Date 2022/9/8
 * @Version 1.0.1
 */
@Service
@AllArgsConstructor
@Transactional(readOnly = true)
public class VerifyService {

    private static final int TODAY_MAX_SEND_VALID_SMS_NUMBER_MOBILE = 30; // 同一个手机号当天最大验证码短信发送量
    private static final int TODAY_MAX_SEND_VALID_SMS_NUMBER_IP = 200; // 同一个IP当天最大验证码短信发送量
    private static final long VERIFY_IMAGE_CACH_KEY_DEL = 3;  // 过期时间为3分钟
    private static final int BASE_IMAGE_WIDTH = 480;   // 底图的宽度
    private static final int BASE_IMAGE_HEIGHT = 240;  // 底图的宽度
    private static final int BASE_IMAGE_TOTAL_NUM = 7;   // 底图总数
    private static final int TEM_IMAGE_TOTAL_NUM = 5;   // 模版图总数
    private static final int BOLD = 0;
    // 手机号格式验证
    //private static final String REGEX = "^((13[0-9])|(14[0,1,4-9])|(15[0-3,5-9])|(16[2,5,6,7])|(17[0-8])|(18[0-9])|(19[0-3,5-9]))\\d{8}$";

    private final UserMapper userMapper;
    private final SmsLogMapper smsLogMapper;
    private final SmsTemplate smsTemplate;
    private final SaltRedis saltRedis;

    @Transactional(readOnly = false, rollbackFor = Exception.class)
    public AjaxJson validate(HttpServletRequest request, String mobile, String smsType){

        if (StringUtils.isBlank(mobile))
            return AjaxJson.error("手机号不能为空!");
        // 验证该手机号是否已注册
        if (Objects.equals(smsType, "1") && userMapper.countByMobile(mobile) > 0)
            return AjaxJson.error("该手机号已注册，无法重复注册!");
        if (Objects.equals(smsType, "2") && userMapper.countByMobile(mobile) < 1)
            return AjaxJson.error("该手机号未注册，是否注册!");

        // 校验安全验证
        // 如果加校验安全验证，则需设置在某一段时间内可以发送手机验证码，或者是每发送一次，安全验证一次
        /*if (!saltRedis.exists(this.key(captureId, mobile)))
            return AjaxJson.error("安全验证已失效，请重新验证!");*/

        //去掉所有空格 \\s* 可以匹配空格、制表符、换页符等空白字符的其中任意一个
        mobile = mobile.replaceAll("\\s*", "");

        // 验证同一个手机号相同类型当天发送短信验证码的次数限制
        String dayDate = DateUtils.getDate();
        long todaySendSmsNumberByMobile = smsLogMapper.daySmsCountByMobile(new SmsLog()
                .setMobile(mobile)
                .setSmsType(smsType)
                .setStartDate(Merge.string(dayDate, " 00:00:00"))
                .setEndDate(Merge.string(dayDate, " 23:59:59")));
        if (todaySendSmsNumberByMobile > TODAY_MAX_SEND_VALID_SMS_NUMBER_MOBILE)
            return AjaxJson.error("今日发送短信验证码次数已达到上限");

        String ip = request.getRemoteAddr();
        // 验证同一个IP当天发送短信验证码的次数
        long todaySendSmsNumberByIp = smsLogMapper.daySmsCountByMobile(new SmsLog()
                .setIp(ip)
                .setStartDate(Merge.string(dayDate, " 00:00:00"))
                .setEndDate(Merge.string(dayDate, " 23:59:59")));
        if (todaySendSmsNumberByIp > TODAY_MAX_SEND_VALID_SMS_NUMBER_IP)
            return AjaxJson.error("今日发送短信验证码次数已达到上限");

        // 生成6位手机验证码
        String smsCode = RandomUtil.randomNumbers(6);
        // 保存发送验证码记录
        SmsLog smsLog = new SmsLog().setId(IdGen.uuid())
                .setMobile(mobile)
                .setSmsType(smsType)
                .setRecDate(new Date())
                .setSmsCode(smsCode)
                .setIp(ip);
        smsLogMapper.insert(smsLog);
        // 发送验证码
        Map<String, String> map = new HashMap<>();
        map.put("code", smsCode);
        SmsData smsData = new SmsData(map).setKey("code");
        SmsCode validate = smsTemplate.sendValidate(smsData, mobile);
        return AjaxJson.success().put("validate", validate.getId());
    }

    // 获取安全验证的图片
    @SneakyThrows
    public AjaxJson capture(String account){

        if (StringUtils.isBlank(account))
            return AjaxJson.error("手机号/账号不能为空!");
        account = account.replaceAll("\\s*", "");

        /*String templatePath = String.format(ImageConstant.CLASS_PATH_URL_TEMPLATE,
                IndexUtil.createIndex(TEM_IMAGE_TOTAL_NUM, 1));
        File templateFile = new ClassPathResource(templatePath).getFile();
        String targetPath = String.format(ImageConstant.CLASS_PATH_URL_TARGET,
                IndexUtil.createIndex(BASE_IMAGE_TOTAL_NUM, 1));
        File targetFile = new ClassPathResource(targetPath).getFile();
        Map<String, Object> map = ImageUtil.cutPicture(templateFile,
                                                        targetFile,
                                                        BASE_IMAGE_WIDTH,
                                                        BASE_IMAGE_HEIGHT,
                                                        BOLD);*/


        String templatePath = String.format(ImageConstant.CLASS_PATH_URL_TEMPLATE,
                IndexUtil.createIndex(TEM_IMAGE_TOTAL_NUM, 1));
        InputStream templateInputStream = new ClassPathResource(templatePath).getInputStream();
        String targetPath = String.format(ImageConstant.CLASS_PATH_URL_TARGET,
                IndexUtil.createIndex(BASE_IMAGE_TOTAL_NUM, 1));
        InputStream targetInputStream = new ClassPathResource(targetPath).getInputStream();
        InputStream targetInputStream1 = new ClassPathResource(targetPath).getInputStream();
        InputStream targetInputStream2 = new ClassPathResource(targetPath).getInputStream();
        Map<String, Object> map = ImageUtil.cutPicture(templateInputStream,
                                                        targetInputStream,
                                                        targetInputStream1,
                                                        targetInputStream2,
                                                        BASE_IMAGE_WIDTH,
                                                        BASE_IMAGE_HEIGHT,
                                                        BOLD);
        /*String templatePath = String.format(ImageConstant.CLASS_PATH_URL_TEMPLATE,
                IndexUtil.createIndex(TEM_IMAGE_TOTAL_NUM, 1));
        ResourcePatternResolver templateResolver = new PathMatchingResourcePatternResolver();
        Resource templateResource = templateResolver.getResources(templatePath)[0];
        InputStream  templateInputStream = templateResource.getInputStream();

        String targetPath = String.format(ImageConstant.CLASS_PATH_URL_TARGET,
                IndexUtil.createIndex(BASE_IMAGE_TOTAL_NUM, 1));
        ResourcePatternResolver targetResolver = new PathMatchingResourcePatternResolver();
        Resource targetResource = targetResolver.getResources(targetPath)[0];
        InputStream targetInputStream = targetResource.getInputStream();
        Map<String, Object> map = ImageUtil.cutPicture(templateInputStream,
                targetInputStream,
                targetPath,
                BASE_IMAGE_WIDTH,
                BASE_IMAGE_HEIGHT,
                BOLD);*/

        String key = this.key((String) map.get("captureId"), account);
        // 先判断是否存在该key,如果存在，则先删掉，在添加
        if (saltRedis.exists(key))
            saltRedis.del(key);
        saltRedis.set(key, map.get("x"), Duration.ofMinutes(VERIFY_IMAGE_CACH_KEY_DEL));
        map.remove("x");
        return AjaxJson.success(map);
    }

    /**
     * 安全验证
     * @param captureId 验证码ID
     * @param location 位置
     * @param account 手机号或账号
     */
    public AjaxJson captureValidate(String captureId, Integer location, String account){

        if (StringUtils.isBlank(account))
            return AjaxJson.error("手机号/账号不能为空!");
        account = account.replaceAll("\\s*", "");

        int x = saltRedis.get(key(captureId, account));
        if (location - 1 <= x && x <= location + 1) { // 误差加减1
            saltRedis.del(key(captureId, account));
            return AjaxJson.success("验证成功!");
        }
        return AjaxJson.error("验证失败!");
    }

    /**
     *
     * @param captureId 验证码ID
     * @param account 手机号或账号
     */
    private String key(String captureId, String account) {
        return ImageConstant.SALT_VERIFY_IMAGE_CACH + account + ":" + captureId;
    }


}
